package com.qsl.ssyx.order.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qsl.ssyx.Service.RabbitService;
import com.qsl.ssyx.client.activity.ActivityFeignClient;
import com.qsl.ssyx.client.cart.CartFeignClient;
import com.qsl.ssyx.client.product.ProductFeignClient;
import com.qsl.ssyx.client.user.UserFeignClient;
import com.qsl.ssyx.common.Interceptor.AuthContextHolder;
import com.qsl.ssyx.common.constant.RedisConst;
import com.qsl.ssyx.common.exception.BusinessException;
import com.qsl.ssyx.common.result.ResultCodeEnum;
import com.qsl.ssyx.common.utils.DateUtil;
import com.qsl.ssyx.constant.MqConst;
import com.qsl.ssyx.enums.*;
import com.qsl.ssyx.model.activity.ActivityRule;
import com.qsl.ssyx.model.activity.CouponInfo;
import com.qsl.ssyx.model.order.CartInfo;
import com.qsl.ssyx.model.order.OrderInfo;
import com.qsl.ssyx.model.order.OrderItem;
import com.qsl.ssyx.order.mapper.OrderInfoMapper;
import com.qsl.ssyx.order.service.OrderInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qsl.ssyx.order.service.OrderItemService;
import com.qsl.ssyx.vo.order.CartInfoVo;
import com.qsl.ssyx.vo.order.OrderConfirmVo;
import com.qsl.ssyx.vo.order.OrderSubmitVo;
import com.qsl.ssyx.vo.order.OrderUserQueryVo;
import com.qsl.ssyx.vo.product.SkuStockLockVo;
import com.qsl.ssyx.vo.user.LeaderAddressVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author 青衫泪
 * @since 2023-09-08
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private CartFeignClient cartFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ActivityFeignClient activityFeignClient;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private OrderItemService orderItemService;


    //    确认订单（立即购买）
    @Override

    public OrderConfirmVo confirmOrder(Long userId) {

        // 根据用户id获取团长信息
        LeaderAddressVo leaderAddressVo = userFeignClient.getUserAddressByUserId(userId);

        // 根据用户id获取购物车选中的商品
        List<CartInfo> cartInfoList = cartFeignClient.getCartCheckedList(userId);

        // 订单唯一标识
        String orderNo = System.currentTimeMillis() + "";
        // TODO Redis字符串存储订单的orderNo 1天，超时不付款订单取消     order:repeat: 1 ，1
        redisTemplate.opsForValue().set(RedisConst.ORDER_REPEAT + orderNo, orderNo,
                24, TimeUnit.HOURS);

        // 根据购物车商品和用户id,获取当前用户购物车商品的活动及优惠卷信息
        OrderConfirmVo orderConfirmVo = activityFeignClient.findCartActivityAndCoupon(cartInfoList, userId);
        // 封装返回值
        orderConfirmVo.setLeaderAddressVo(leaderAddressVo);
        orderConfirmVo.setOrderNo(orderNo);
        return orderConfirmVo;
    }

    //    生成订单
    @Override
    public Long submitOrder(OrderSubmitVo orderSubmitVo, Long userId) {
        // 一： 判断订单是否重复提交 （通过Redis+lua脚本判断）   注意：lua脚本也是Redis的，lua脚本可以保证原子性操作。
        // 获取订单id(唯一)
        String orderNo = orderSubmitVo.getOrderNo();
        // 判断订单id为空,抛异常。
        if (StringUtils.isEmpty(orderNo)) {
            throw new BusinessException(ResultCodeEnum.ILLEGAL_REQUEST);
        }
//        lua脚本判断原子性
        // 如果redis有相同order,表示正常提交订单，把redis的orderNo删除
        // 如果redis没有相同orderNo,表示重复提交了，报异常
        String script = "if(redis.call('get', KEYS[1]) == ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end";
        // order:repeat:5,5
        Boolean flag = (Boolean) redisTemplate.execute(
                new DefaultRedisScript(script, Boolean.class),
                Arrays.asList(RedisConst.ORDER_REPEAT + orderNo), orderNo);
        if (!flag) {
            throw new BusinessException(ResultCodeEnum.REPEAT_SUBMIT);
        }

        //  二： 验证库存，并且锁定库存
        // 比如仓库有10个西红柿，我想买2个西红柿
        // ** 验证库存，查询仓库里面是是否有充足西红柿
        // ** 库存充足，库存锁定 2锁定（目前没有真正减库存）
        //1、根据用户id,获取当前用户选中购物车选中选中的商品
        List<CartInfo> cartInfoList = cartFeignClient.getCartCheckedListByUserId(userId);

        // 2.获取购物车的普通类型商品，  秒杀商品先不做    ==》 商品类型：0->普通商品 1->秒杀商品
        List<CartInfo> commonSkuList = cartInfoList.stream()
                .filter(item -> item.getSkuType().equals(SkuType.COMMON.getCode()))
                .collect(Collectors.toList());

        // 3.List<CartInfo>转换 List<SkuStockLockVo>
        if (!CollectionUtils.isEmpty(commonSkuList)) {
            List<SkuStockLockVo> commonskuStockLockVoList = commonSkuList.stream().map(item -> {
                SkuStockLockVo skuStockLockVo = new SkuStockLockVo();
                skuStockLockVo.setSkuId(item.getSkuId());
                skuStockLockVo.setSkuNum(item.getSkuNum());
                return skuStockLockVo;
            }).collect(Collectors.toList());
            // 公平锁锁定商品
            Boolean isLockSuccess = productFeignClient.checkAndLock(commonskuStockLockVoList, orderNo);
            if (!isLockSuccess) { // 库存锁定失败
                throw new BusinessException(ResultCodeEnum.ORDER_STOCK_FALL);
            }
        }

        // 商品下单  ====》  order_info 和 order_item
        Long orderId = this.saveOrder(orderSubmitVo, cartInfoList);

        // TODO 支付成功 rabbitMq发送消息①修改订单状态（待发货），②减库存，③ 删除购物车已购买的商品记录   ==>   交换机,路由key,消息
        rabbitService.sendMessage(MqConst.EXCHANGE_ORDER_DIRECT, MqConst.ROUTING_DELETE_CART, orderSubmitVo.getUserId());

        // 返回订单Id
        return orderId;
    }

    //    根据orderNo（订单号）查询订单信息
    @Override
    public OrderInfo getOrderInfoByorderNo(String orderNo) {
        LambdaQueryWrapper<OrderInfo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(OrderInfo::getOrderNo, orderNo);

        OrderInfo orderInfo = baseMapper.selectOne(lqw);
        return orderInfo;
    }

    //  支付成功 rabbitMq发送消息①修改订单状态（待发货），②减库存，③ 删除购物车已购买的商品记录   ==>   交换机,路由key,消息
    @Override
    public void orderPay(String orderNo) {
        // 根据订单号查询订单信息
        OrderInfo orderInfo = this.getOrderInfoByorderNo(orderNo);
        // 不为空并且订单状态不为待支付
        if (orderInfo == null || orderInfo.getOrderStatus() != OrderStatus.UNPAID) {
            return;
        }

        // 根据订单ID，修改订单状态
        orderInfo.setOrderStatus(OrderStatus.WAITING_DELEVER); // 订单状态（待发货）
        orderInfo.setProcessStatus(ProcessStatus.WAITING_DELEVER); // 订单状态（待发货）
        int i = baseMapper.updateById(orderInfo);

        //   TODO 支付成功 rabbitMq发送消息②减库存，③ 删除购物车已购买的商品记录   ==>   交换机,路由key,消息  ===》 ssyx.order.direct，ssyx.minus.stock,订单号
        rabbitService.sendMessage(MqConst.EXCHANGE_ORDER_DIRECT, MqConst.ROUTING_MINUS_STOCK, orderNo);
    }

    // 根据订单状态查询，订单信息  ==>  订单状态【0->待付款；1->待发货；2->已发货；3->待用户收货，已完成】
    @Override
    public Page<OrderInfo> findUserOrderByOrderStatusId(Page<OrderInfo> pageParam, OrderUserQueryVo orderUserQueryVo) {
        // 获取用户ID
        Long userId = AuthContextHolder.getUserId();
        // 根据用户ID和状态查询订单信息
        LambdaQueryWrapper<OrderInfo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(OrderInfo::getUserId, userId);
        lqw.eq(OrderInfo::getOrderStatus, orderUserQueryVo.getOrderStatus());
        Page<OrderInfo> orderInfoPage = baseMapper.selectPage(pageParam, lqw);

        // 获取订单id集合
        List<OrderInfo> orderInfoList = orderInfoPage.getRecords();
        for (OrderInfo orderInfo : orderInfoList) {// 根据订单id集合查询订单商品
            List<OrderItem> orderItemList = orderItemService.getOrderItemByOrderInfoId(orderInfo.getId());

            orderInfo.setOrderItemList(orderItemList); // 商品集合
            orderInfo.getParam().put("orderStatusName", orderInfo.getOrderStatus().getComment()); // 订单状态
        }

        return orderInfoPage;
    }


    // 商品下单  ====》  order_info 和 order_item
    @Transactional(rollbackFor = {Exception.class})
    public Long saveOrder(OrderSubmitVo orderParamVo, List<CartInfo> cartInfoList) {

        if (CollectionUtils.isEmpty(cartInfoList)) { // 数据为空，报异常
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
        }

        //  查询用户提货点和团长信息
        Long userId = AuthContextHolder.getUserId();
        LeaderAddressVo leaderAddressVo = userFeignClient.getUserAddressByUserId(userId);
        if (leaderAddressVo == null) {
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
        }
        // 营销活动金额
        Map<String, BigDecimal> activitySplitAmount = this.computeActivitySplitAmount(cartInfoList);
        // 优惠卷金额
        Map<String, BigDecimal> couponInfoSplitAmount = this.computeCouponInfoSplitAmount(cartInfoList, orderParamVo.getCouponId());

        // 封装商品数据
        List<OrderItem> orderItemList = new ArrayList<>();
        BeanUtils.copyProperties(cartInfoList, orderItemList);
        // 团长id
        orderItemList.stream().forEach(item -> item.setLeaderId(orderParamVo.getLeaderId()));
        // 营销活动金额,优惠卷金额
        for (OrderItem orderItem : orderItemList) {
            BigDecimal activityAmount = activitySplitAmount.get("activity:" + orderItem);
            if (activitySplitAmount == null) {
                activityAmount = new BigDecimal(0);
            }
            // 营销活动金额
            orderItem.setSplitActivityAmount(activityAmount);

            // 优惠卷金额
            BigDecimal couponAmount = couponInfoSplitAmount.get("coupon:" + orderItem.getSkuId());
            if (couponAmount == null) {
                couponAmount = new BigDecimal(0);
            }
            orderItem.setSplitCouponAmount(couponAmount);

            // 总金额  = skuPrice × 商品购买的数量
            BigDecimal skuTotalAmount = orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum()));
            // 优惠后金额 =  总金额 - 营销活动金额 - 优惠卷金额
            BigDecimal splitTotalAmount = skuTotalAmount.subtract(activityAmount).subtract(couponAmount);
            orderItem.setSplitTotalAmount(splitTotalAmount);
            orderItemList.add(orderItem);
        }

        // 封装订单OrderInfo数据
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(userId);
        BeanUtils.copyProperties(orderParamVo, orderInfo); // 主要复制： 订单号，团长id，收货人姓名，收货人电话,下单选中的优惠券id
        BeanUtils.copyProperties(leaderAddressVo, orderInfo); // 主要复制：团长名称,省,城市,区域,详细地址,仓库id
        orderInfo.setProcessStatus(ProcessStatus.UNPAID); // 订单状态(进度状态): 待付款

        // 订单金额
        BigDecimal originalTotalAmount = this.computeTotalAmount(cartInfoList);
        BigDecimal activityAmount = activitySplitAmount.get("activity:total");
        orderInfo.setOriginalTotalAmount(originalTotalAmount);

        // 优惠卷金额
        if (activityAmount == null) {
            activityAmount = new BigDecimal(0);
        }
        BigDecimal couponAmount = couponInfoSplitAmount.get("coupon:total");
        orderInfo.setActivityAmount(couponAmount);

        if (couponAmount == null) {
            couponAmount = new BigDecimal(0);
        }
        //    减于
        BigDecimal totalAmount = originalTotalAmount.subtract(activityAmount).subtract(couponAmount);
        orderInfo.setTotalAmount(totalAmount);

        // 计算团长佣金   = 订单总额 × 0
        BigDecimal profitRate = new BigDecimal(0);
        BigDecimal commissionAmount = orderInfo.getTotalAmount().multiply(profitRate);
        orderInfo.setCommissionAmount(commissionAmount);

        // 新增订单基本信息
        baseMapper.insert(orderInfo);

        // 添加订单里面订单项
        orderItemList.forEach(orderItem -> {
            orderItem.setOrderId(orderInfo.getId());
            orderItemService.save(orderItem);
        });

        // 商品使用优惠卷，修改优惠卷状态
        if (orderInfo.getCouponId() != null) {
            //  根据用户id、优惠卷id、优惠卷与用户ID， 修改优惠卷状态-已使用
            activityFeignClient.updateCouponInfoUseStatus(orderInfo.getCouponId(), userId, orderInfo.getId());
        }

        // 下单成功，redis的hash类型记录用户购物商品数量   ===》key(userId)  -  field(skuId)-value(skuNum)
        String orderSkuKey = RedisConst.ORDER_SKU_MAP + orderParamVo.getUserId();
        BoundHashOperations<String, String, Integer> hashOperations = redisTemplate.boundHashOps(orderSkuKey);
        cartInfoList.forEach(cartInfo -> {
            //     判断当前集合中是否已经存在hashkey
            if (hashOperations.hasKey(cartInfo.getSkuId().toString())) {
                Integer orderSkuNum = hashOperations.get(cartInfo.getSkuId().toString() + cartInfo.getSkuNum());
                hashOperations.put(cartInfo.getSkuId().toString(), orderSkuNum);
            }
        });
        //  为给定的key设置过期时间：key, 获取当前时间到晚上23点59分59秒的时间间隔, 秒
        redisTemplate.expire(orderSkuKey, DateUtil.getCurrentExpireTimes(), TimeUnit.SECONDS);
        // 订单ID
        return orderInfo.getId();
    }

    // 优惠卷金额
    private Map<String, BigDecimal> computeCouponInfoSplitAmount(List<CartInfo> cartInfoList, Long couponId) {
        Map<String, BigDecimal> couponInfoSplitAmountMap = new HashMap<>();

        if (couponId == null) {
            return couponInfoSplitAmountMap;
        }

        // 根据购物车商品和优惠卷id获取优惠卷信息
        CouponInfo couponInfo = activityFeignClient.findRangeSkuIdList(cartInfoList, couponId);

        if (null != couponInfo) {
            //sku对应的订单明细
            Map<Long, CartInfo> skuIdToCartInfoMap = new HashMap<>();
            for (CartInfo cartInfo : cartInfoList) {
                skuIdToCartInfoMap.put(cartInfo.getSkuId(), cartInfo);
            }
            //优惠券对应的skuId列表
            List<Long> skuIdList = couponInfo.getSkuIdList();
            if (CollectionUtils.isEmpty(skuIdList)) {
                return couponInfoSplitAmountMap;
            }
            //优惠券优化总金额
            BigDecimal reduceAmount = couponInfo.getAmount();
            if (skuIdList.size() == 1) {
                //sku的优化金额
                couponInfoSplitAmountMap.put("coupon:" + skuIdToCartInfoMap.get(skuIdList.get(0)).getSkuId(), reduceAmount);
            } else {
                //总金额
                BigDecimal originalTotalAmount = new BigDecimal(0);
                for (Long skuId : skuIdList) {
                    CartInfo cartInfo = skuIdToCartInfoMap.get(skuId);
                    BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                    originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
                }
                //记录除最后一项是所有分摊金额， 最后一项=总的 - skuPartReduceAmount
                BigDecimal skuPartReduceAmount = new BigDecimal(0);
                if (couponInfo.getCouponType() == CouponType.CASH || couponInfo.getCouponType() == CouponType.FULL_REDUCTION) {
                    for (int i = 0, len = skuIdList.size(); i < len; i++) {
                        CartInfo cartInfo = skuIdToCartInfoMap.get(skuIdList.get(i));
                        if (i < len - 1) {
                            BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                            //sku分摊金额
                            BigDecimal skuReduceAmount = skuTotalAmount.divide(originalTotalAmount, 2, RoundingMode.HALF_UP).multiply(reduceAmount);
                            couponInfoSplitAmountMap.put("coupon:" + cartInfo.getSkuId(), skuReduceAmount);

                            skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                        } else {
                            BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                            couponInfoSplitAmountMap.put("coupon:" + cartInfo.getSkuId(), skuReduceAmount);
                        }
                    }
                }
            }
            couponInfoSplitAmountMap.put("coupon:total", couponInfo.getAmount());
        }
        return couponInfoSplitAmountMap;
    }

    // 计算总金额
    private BigDecimal computeTotalAmount(List<CartInfo> cartInfoList) {
        BigDecimal total = new BigDecimal(0);
        for (CartInfo cartInfo : cartInfoList) {
//               乘于 商品数量
            BigDecimal itemTotal = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
            total = total.add(itemTotal);
        }
        return total;
    }

    /**
     * 营销活动金额
     * 计算购物商品分摊的优惠减少金额
     * 打折：按折扣分担
     * 现金：按比例分摊
     */
    private Map<String, BigDecimal> computeActivitySplitAmount(List<CartInfo> cartInfoParamList) {
        Map<String, BigDecimal> activitySplitAmountMap = new HashMap<>();

        // 促销活动相关信息
        List<CartInfoVo> cartInfoVoList = activityFeignClient.findCartActivityList(cartInfoParamList);

        //活动总金额
        BigDecimal activityReduceAmount = new BigDecimal(0);
        if (!CollectionUtils.isEmpty(cartInfoVoList)) {
            for (CartInfoVo cartInfoVo : cartInfoVoList) {
                ActivityRule activityRule = cartInfoVo.getActivityRule(); // 活动规则
                List<CartInfo> cartInfoList = cartInfoVo.getCartInfoList(); // 购物项凑单，同一活动对应的最优活动规则

                if (activityRule != null) { // 优惠金额，按比例分摊
                    BigDecimal reduceAmount = activityRule.getReduceAmount(); // 优惠后减少金额
                    activityReduceAmount = activityReduceAmount.add(reduceAmount);

                    if (cartInfoList.size() == 1) {
                        activitySplitAmountMap.put("activity:" + cartInfoList.get(0).getSkuId(), reduceAmount);
                    } else {
                        // 总金额
                        BigDecimal originalTotalAmount = new BigDecimal(0);
                        for (CartInfo cartInfo : cartInfoList) {
                            // 商品总金额  = 金额 乘于 商品数量
                            BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                            originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
                        }

                        //记录除最后一项是所有分摊金额， 最后一项=总的 - skuPartReduceAmount
                        BigDecimal skuPartReduceAmount = new BigDecimal(0);
                        if (activityRule.getActivityType() == ActivityType.FULL_REDUCTION) { // 满减
                            for (int i = 0, len = cartInfoList.size(); i < len; i++) {
                                CartInfo cartInfo = cartInfoList.get(i);

                                if (i < len - 1) {
                                    // 商品总金额  = 金额 乘于 商品数量
                                    BigDecimal sktTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                                    // sku分摊金额  =   商品总金额  除以     乘于  优惠后减少金额
                                    // divide ：第一个参数是除数，第二个参数代表保留几位小数，第三个代表的是使用的模式。
                                    // BigDecimal.ROUND_UP:直接进位，比如1.21如果保留1位小数，得到的就是1.3
                                    BigDecimal skuReduceAmount = sktTotalAmount.divide(originalTotalAmount, 2, RoundingMode.HALF_UP).multiply(reduceAmount);
                                    activitySplitAmountMap.put("activity:" + cartInfo.getSkuId(), skuReduceAmount);

                                    skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                                } else {
//                                    优惠后金额 减于
                                    BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                                    activitySplitAmountMap.put("activity:" + cartInfo.getSkuId(), skuReduceAmount);
                                }
                            }
                        } else {
                            for (int i = 0, len = cartInfoList.size(); i < len; i++) {
                                CartInfo cartInfo = cartInfoList.get(i);
                                if (i < len - 1) {
                                    //  购物车商品价格 乘于 商品数量
                                    BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));

                                    // sku分摊金额
                                    BigDecimal skuDiscountTotalAmount = skuTotalAmount.multiply(activityRule.getBenefitDiscount().divide(new BigDecimal("10")));
                                    BigDecimal skuReduceAmount = skuTotalAmount.subtract(skuDiscountTotalAmount);
                                    activitySplitAmountMap.put("activity:" + cartInfo.getSkuId(), skuReduceAmount);

                                    skuReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                                } else {
                                    BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                                    activitySplitAmountMap.put("activity:" + cartInfo.getSkuId(), skuReduceAmount);
                                }
                            }
                        }
                    }
                }
            }

        }

        activitySplitAmountMap.put("activity:total", activityReduceAmount);
        return activitySplitAmountMap;
    }
}
